const jwt = require('jsonwebtoken');
const { promisify } = require('util');
const config = require('../config/config');
const logger = require('../config/logger');
const redis = require('../config/redis');
const { AuthenticationError, AuthorizationError } = require('./errorHandler');

// JWT工具函数
const jwtUtils = {
  // 生成访问令牌
  generateAccessToken: (payload) => {
    return jwt.sign(payload, config.jwt.secret, {
      expiresIn: config.jwt.expiresIn,
      issuer: config.jwt.issuer,
      audience: config.jwt.audience
    });
  },
  
  // 生成刷新令牌
  generateRefreshToken: (payload) => {
    return jwt.sign(payload, config.jwt.secret, {
      expiresIn: config.jwt.refreshExpiresIn,
      issuer: config.jwt.issuer,
      audience: config.jwt.audience
    });
  },
  
  // 验证令牌
  verifyToken: promisify(jwt.verify),
  
  // 解码令牌（不验证）
  decodeToken: (token) => {
    return jwt.decode(token, { complete: true });
  },
  
  // 获取令牌过期时间
  getTokenExpiration: (token) => {
    const decoded = jwt.decode(token);
    return decoded ? new Date(decoded.exp * 1000) : null;
  }
};

// 从请求中提取令牌
const extractToken = (req) => {
  let token = null;
  
  // 从Authorization头部提取
  const authHeader = req.headers.authorization;
  if (authHeader && authHeader.startsWith('Bearer ')) {
    token = authHeader.substring(7);
  }
  
  // 从Cookie中提取（备选方案）
  if (!token && req.cookies && req.cookies.accessToken) {
    token = req.cookies.accessToken;
  }
  
  // 从查询参数中提取（仅限特殊情况，如文件下载）
  if (!token && req.query.token) {
    token = req.query.token;
  }
  
  return token;
};

// 检查令牌是否在黑名单中
const isTokenBlacklisted = async (token) => {
  try {
    const blacklisted = await redis.get(`blacklist:${token}`);
    return blacklisted !== null;
  } catch (error) {
    logger.error('检查令牌黑名单失败:', error);
    return false; // 如果Redis出错，不阻止访问
  }
};

// 将令牌加入黑名单
const blacklistToken = async (token, expiresIn = null) => {
  try {
    const expiration = expiresIn || jwtUtils.getTokenExpiration(token);
    const ttl = expiration ? Math.floor((expiration.getTime() - Date.now()) / 1000) : 86400; // 默认24小时
    
    if (ttl > 0) {
      await redis.set(`blacklist:${token}`, '1', ttl);
      logger.info('令牌已加入黑名单', { token: token.substring(0, 20) + '...', ttl });
    }
  } catch (error) {
    logger.error('加入令牌黑名单失败:', error);
  }
};

// 认证中间件
const authenticate = async (req, res, next) => {
  try {
    // 提取令牌
    const token = extractToken(req);
    
    if (!token) {
      throw new AuthenticationError('访问令牌缺失');
    }
    
    // 检查令牌是否在黑名单中
    if (await isTokenBlacklisted(token)) {
      throw new AuthenticationError('访问令牌已失效');
    }
    
    // 验证令牌
    const decoded = await jwtUtils.verifyToken(token, config.jwt.secret);
    
    // 检查令牌的issuer和audience
    if (decoded.iss !== config.jwt.issuer || decoded.aud !== config.jwt.audience) {
      throw new AuthenticationError('访问令牌无效');
    }
    
    // 将用户信息附加到请求对象
    req.user = {
      id: decoded.userId,
      email: decoded.email,
      role: decoded.role,
      permissions: decoded.permissions || [],
      tokenId: decoded.jti,
      issuedAt: new Date(decoded.iat * 1000),
      expiresAt: new Date(decoded.exp * 1000)
    };
    
    req.token = token;
    
    // 记录认证成功日志
    logger.debug('用户认证成功', {
      userId: req.user.id,
      email: req.user.email,
      role: req.user.role,
      requestId: req.requestId
    });
    
    next();
  } catch (error) {
    // 记录认证失败日志
    logger.security('认证失败', {
      error: error.message,
      ip: req.ip,
      userAgent: req.get('User-Agent'),
      url: req.originalUrl,
      requestId: req.requestId
    });
    
    if (error.name === 'JsonWebTokenError') {
      next(new AuthenticationError('访问令牌无效'));
    } else if (error.name === 'TokenExpiredError') {
      next(new AuthenticationError('访问令牌已过期'));
    } else if (error.name === 'NotBeforeError') {
      next(new AuthenticationError('访问令牌尚未生效'));
    } else {
      next(error);
    }
  }
};

// 可选认证中间件（不强制要求认证）
const optionalAuthenticate = async (req, res, next) => {
  try {
    const token = extractToken(req);
    
    if (token && !(await isTokenBlacklisted(token))) {
      const decoded = await jwtUtils.verifyToken(token, config.jwt.secret);
      
      req.user = {
        id: decoded.userId,
        email: decoded.email,
        role: decoded.role,
        permissions: decoded.permissions || [],
        tokenId: decoded.jti,
        issuedAt: new Date(decoded.iat * 1000),
        expiresAt: new Date(decoded.exp * 1000)
      };
      
      req.token = token;
    }
  } catch (error) {
    // 可选认证失败时不抛出错误，只记录日志
    logger.debug('可选认证失败', {
      error: error.message,
      ip: req.ip,
      requestId: req.requestId
    });
  }
  
  next();
};

// 授权中间件
const authorize = (requiredRoles = [], requiredPermissions = []) => {
  return (req, res, next) => {
    try {
      if (!req.user) {
        throw new AuthenticationError('用户未认证');
      }
      
      const { role, permissions = [] } = req.user;
      
      // 检查角色权限
      if (requiredRoles.length > 0) {
        const hasRequiredRole = requiredRoles.includes(role) || role === 'super_admin';
        
        if (!hasRequiredRole) {
          logger.security('权限不足 - 角色检查失败', {
            userId: req.user.id,
            userRole: role,
            requiredRoles,
            ip: req.ip,
            url: req.originalUrl,
            requestId: req.requestId
          });
          
          throw new AuthorizationError('权限不足');
        }
      }
      
      // 检查具体权限
      if (requiredPermissions.length > 0) {
        const hasAllPermissions = requiredPermissions.every(permission => 
          permissions.includes(permission) || permissions.includes('*')
        );
        
        if (!hasAllPermissions && role !== 'super_admin') {
          logger.security('权限不足 - 权限检查失败', {
            userId: req.user.id,
            userPermissions: permissions,
            requiredPermissions,
            ip: req.ip,
            url: req.originalUrl,
            requestId: req.requestId
          });
          
          throw new AuthorizationError('权限不足');
        }
      }
      
      // 记录授权成功日志
      logger.debug('用户授权成功', {
        userId: req.user.id,
        role,
        requiredRoles,
        requiredPermissions,
        requestId: req.requestId
      });
      
      next();
    } catch (error) {
      next(error);
    }
  };
};

// 检查资源所有权
const checkOwnership = (getResourceOwnerId) => {
  return async (req, res, next) => {
    try {
      if (!req.user) {
        throw new AuthenticationError('用户未认证');
      }
      
      // 超级管理员跳过所有权检查
      if (req.user.role === 'super_admin') {
        return next();
      }
      
      const resourceOwnerId = await getResourceOwnerId(req);
      
      if (resourceOwnerId !== req.user.id) {
        logger.security('资源访问被拒绝 - 非资源所有者', {
          userId: req.user.id,
          resourceOwnerId,
          resource: req.originalUrl,
          ip: req.ip,
          requestId: req.requestId
        });
        
        throw new AuthorizationError('无权访问此资源');
      }
      
      next();
    } catch (error) {
      next(error);
    }
  };
};

// 速率限制中间件（基于用户）
const userRateLimit = (maxRequests = 100, windowMs = 15 * 60 * 1000) => {
  return async (req, res, next) => {
    try {
      if (!req.user) {
        return next();
      }
      
      const key = `rate_limit:user:${req.user.id}`;
      const current = await redis.incr(key);
      
      if (current === 1) {
        await redis.expire(key, Math.floor(windowMs / 1000));
      }
      
      if (current > maxRequests) {
        logger.security('用户速率限制触发', {
          userId: req.user.id,
          requests: current,
          maxRequests,
          ip: req.ip,
          requestId: req.requestId
        });
        
        return res.status(429).json({
          success: false,
          error: {
            message: '请求过于频繁，请稍后再试',
            code: 'USER_RATE_LIMIT_EXCEEDED',
            retryAfter: Math.floor(windowMs / 1000)
          }
        });
      }
      
      // 设置响应头
      res.set({
        'X-RateLimit-Limit': maxRequests,
        'X-RateLimit-Remaining': Math.max(0, maxRequests - current),
        'X-RateLimit-Reset': new Date(Date.now() + windowMs).toISOString()
      });
      
      next();
    } catch (error) {
      logger.error('用户速率限制检查失败:', error);
      next(); // 如果Redis出错，不阻止请求
    }
  };
};

// 刷新令牌中间件
const refreshToken = async (req, res, next) => {
  try {
    const { refreshToken } = req.body;
    
    if (!refreshToken) {
      throw new AuthenticationError('刷新令牌缺失');
    }
    
    // 检查刷新令牌是否在黑名单中
    if (await isTokenBlacklisted(refreshToken)) {
      throw new AuthenticationError('刷新令牌已失效');
    }
    
    // 验证刷新令牌
    const decoded = await jwtUtils.verifyToken(refreshToken, config.jwt.secret);
    
    // 生成新的访问令牌
    const newAccessToken = jwtUtils.generateAccessToken({
      userId: decoded.userId,
      email: decoded.email,
      role: decoded.role,
      permissions: decoded.permissions
    });
    
    // 可选：生成新的刷新令牌
    const newRefreshToken = jwtUtils.generateRefreshToken({
      userId: decoded.userId,
      email: decoded.email,
      role: decoded.role
    });
    
    // 将旧的刷新令牌加入黑名单
    await blacklistToken(refreshToken);
    
    req.tokens = {
      accessToken: newAccessToken,
      refreshToken: newRefreshToken
    };
    
    req.user = {
      id: decoded.userId,
      email: decoded.email,
      role: decoded.role,
      permissions: decoded.permissions || []
    };
    
    logger.info('令牌刷新成功', {
      userId: decoded.userId,
      ip: req.ip,
      requestId: req.requestId
    });
    
    next();
  } catch (error) {
    logger.security('令牌刷新失败', {
      error: error.message,
      ip: req.ip,
      requestId: req.requestId
    });
    
    next(error);
  }
};

module.exports = {
  jwtUtils,
  authenticate,
  optionalAuthenticate,
  authorize,
  checkOwnership,
  userRateLimit,
  refreshToken,
  blacklistToken,
  extractToken
};